package com.pig4cloud.pigx.contract.executer.order.canceler;

import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.customize.exception.MqAckException;
import com.pig4cloud.pigx.common.customize.exception.MqNotAckException;
import com.pig4cloud.pigx.common.customize.mysql.utils.MysqlUtil;
import com.pig4cloud.pigx.common.customize.security.utils.SecurityUtil;
import com.pig4cloud.pigx.common.customize.utils.CommonUtil;
import com.pig4cloud.pigx.common.customize.i18n.util.MessageUtils;
import com.pig4cloud.pigx.common.seata.utils.SeataUtils;
import com.pig4cloud.pigx.contract.constant.ContractMqConstants;
import com.pig4cloud.pigx.contract.constant.I18nMessages;
import com.pig4cloud.pigx.contract.dto.ContractOrderCancelDTO;
import com.pig4cloud.pigx.contract.entity.ContractOrder;
import com.pig4cloud.pigx.contract.enums.ContractOrderEnum;
import com.pig4cloud.pigx.contract.mapper.ContractOrderMapper;
import com.pig4cloud.pigx.contract.mongo.entity.ContractOrderMongo;
import com.pig4cloud.pigx.contract.utils.ContractUtils;
import com.pig4cloud.pigx.wallet.dto.WalletContractAccountChangeDTO;
import com.pig4cloud.pigx.wallet.enums.WalletContractAccountDetailEnum;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * 抽象订单撤销处理器
 *
 * @since 2022/8/17 11:17
 */
@Slf4j
@Component
@AllArgsConstructor
public class OrderCanceler {

	protected final ContractOrderMapper contractOrderMapper;

	protected final MongoTemplate mongo;
	protected final AmqpTemplate rabbit;

	protected final CommonUtil commonUtil;
	protected final MysqlUtil mysqlUtil;
	protected final SecurityUtil securityUtil;
	protected final ContractUtils contractUtils;

	/**
	 * 撤销订单
	 *
	 * @param dto dto
	 * @since 2022/8/17 11:15
	 */
	public void cancel(ContractOrderCancelDTO dto) {
		// 开启本地事务
		mysqlUtil.startLocalTx();

		try {
			ContractOrder order = getOrder(dto);

			// 初始化修改信息
			securityUtil.initUpdateAbout(order);
			if (contractOrderMapper.cancel(order) != 1) {
				throw new MqNotAckException(MessageUtils.get(I18nMessages.ORDER_CANCELLATION_FAILURE));
			}

			// 开仓
			boolean hasRestAmount = false;
			if (Objects.equals(ContractOrderEnum.OperationType.OPEN.getValue(), order.getOperationType())) {
				// 开启全局事务
				SeataUtils.startGlobalTx();
				// 解冻冻结金额
				hasRestAmount = unFreezeRestAmount(order);

			} else {
				// 平仓，回滚仓位平仓中数量
				contractUtils.rollbackPositionClosingNumByOrder(order);
			}

			// 删除相关缓存
			cancelOrderCache(order);

			// 提交全局事务
			SeataUtils.commitGlobalTx();
			// 提交本地事务
			mysqlUtil.commitLocalTx();

			// 发送订单取消深度图消息
			/*contractUtils.sendDepthMap(ContractMqConstants.EXCHANGE_FANOUT_CONTRACT_ORDER_CANCEL_DEPTH_MAP,
					null,
					order);*/

			// 如果有需要解冻的金额
			if (hasRestAmount) {
				// 发送重算强平价消息
				contractUtils.sendPositionCrossCalculate(order.getUserId());
			}

			// 发送历史订单迁移消息
			contractUtils.sendOrderMigrate(Collections.singletonList(dto.getId()));

		} catch (Throwable e) {
			// 回滚全局事务
			SeataUtils.rollbackGlobalTx();
			// 回滚本地事务
			mysqlUtil.rollbackLocalTx();

			throw e;
		}
	}

	/**
	 * 取消订单时，删除订单相关缓存
	 *
	 * @param order 订单
	 * @since 2022/9/3 21:36
	 */
	public void cancelOrderCache(ContractOrder order) {
		// 从对应订单薄删除
		String orderBookName = ContractUtils.getOrderBookName(order.getIsBuy());
		Criteria criteria = Criteria.where("id").is(order.getId());
		mongo.remove(Query.query(criteria), ContractOrderMongo.class, orderBookName);

		// 判断是否是挂单
		boolean isConditionPending = ContractUtils.isConditionPending(order.getHasStopProfit(), order.getHasStopLost());

		// 限价单、条件市价单
		if (ContractUtils.isNeedChangePending(order.getType(), isConditionPending)) {
			// 减少挂单数量
			contractUtils.incrOrderPending(order.getUserId(), -1);
			if (isConditionPending) {
				contractUtils.incrOrderConditionPending(order.getUserId(), -1);
			}
		}

		// 开仓
		if (Objects.equals(ContractOrderEnum.OperationType.OPEN.getValue(), order.getOperationType())) {
			// 减少总持仓数量
			contractUtils.incrPositionTotalNum(order.getUserId(),
					order.getSymbolName(),
					order.getNoDealNum().negate());
		}
	}

	/**
	 * 获取订单信息，并锁定
	 *
	 * @param dto dto
	 * @return {@link ContractOrder}
	 * @since 2022/8/17 21:21
	 */
	public ContractOrder getOrder(ContractOrderCancelDTO dto) {
		if (Objects.isNull(dto.getId())) {
			throw new MqNotAckException(MessageUtils.get(I18nMessages.ID_IS_EMPTY));
		}
		if (Objects.isNull(dto.getUserId())) {
			throw new MqAckException(MessageUtils.get(I18nMessages.USER_ID_IS_EMPTY));
		}

		// 查询订单，并锁定
		ContractOrder condition = new ContractOrder();
		condition.setId(dto.getId());
		// 确保是用户本人的数据
		condition.setUserId(dto.getUserId());
		ContractOrder order = contractUtils.getOneAvailableOrder(condition, true);
		if (Objects.isNull(order)) {
			throw new MqAckException(MessageUtils.get(I18nMessages.ORDER_STATUS_HAS_CHANGED));
		}

		return order;
	}

	/**
	 * 如果未成交，解冻冻结金额
	 *
	 * @param order 订单
	 * @return {@link boolean}
	 * @since 2022/8/30 00:05
	 */
	private boolean unFreezeRestAmount(ContractOrder order) {
		// 如果冻结金额小于等于0，直接结束
		if (order.getFrozenAmount().compareTo(BigDecimal.ZERO) <= 0) {
			return false;
		}

		// 组装参数
		WalletContractAccountChangeDTO param = ContractUtils.getChangeAssetParamByOrder(order,
				order.getFrozenAmount(),
				null,
				null,
				WalletContractAccountDetailEnum.OperationType.UN_FREEZE,
				true);

		R<?> r = contractUtils.changeAsset(Collections.singletonList(param));
		if (!r.getIsSuccess()) {
			throw new RuntimeException(r.getMsg());
		}

		return true;
	}

	/**
	 * 撤销用户所有订单
	 *
	 * @param userId userId
	 * @since 2022/8/17 11:15
	 */
	public void cancelByUser(Long userId) {
		if (Objects.isNull(userId)) {
			throw new RuntimeException(MessageUtils.get(I18nMessages.USER_ID_IS_EMPTY));
		}

		// 根据用户查询可以撤销的订单idList
		List<Long> idList = contractOrderMapper.getNeedCancelOrderIdList(userId);

		// 消息实体
		ContractOrderCancelDTO dto = new ContractOrderCancelDTO();
		dto.setUserId(userId);
		// 遍历，发送消息
		idList.forEach(id -> {
			dto.setId(id);

			rabbit.convertAndSend(ContractMqConstants.EXCHANGE_FANOUT_CONTRACT_ORDER_CANCEL,
					null,
					dto);
		});
	}

	/**
	 * 解冻未成交订单的剩余未解冻金额
	 *
	 * @param list list
	 * @return {@link boolean} true 有剩余金额，false 没有
	 * @since 2022/8/30 00:05
	 */
	private boolean unFreezeRestAmount(List<ContractOrder> list) {
		// 累计总剩余金额
		BigDecimal totalRestAmount = BigDecimal.ZERO;
		for (ContractOrder i : list) {
			// 只有开仓需要冻结
			if (!Objects.equals(ContractOrderEnum.OperationType.OPEN.getValue(), i.getOperationType())) {
				continue;
			}

			// 如果冻结金额大于0，累计
			if (i.getFrozenAmount().compareTo(BigDecimal.ZERO) > 0) {
				totalRestAmount = totalRestAmount.add(i.getFrozenAmount());
			}
		}

		if (totalRestAmount.compareTo(BigDecimal.ZERO) == 0) {
			return false;
		}

		// 组装参数，订单信息使用第一个就可以
		WalletContractAccountChangeDTO param = ContractUtils.getChangeAssetParamByOrder(list.get(0),
				totalRestAmount,
				null,
				null,
				WalletContractAccountDetailEnum.OperationType.UN_FREEZE,
				true);

		R<?> r = contractUtils.changeAsset(Collections.singletonList(param));
		if (!r.getIsSuccess()) {
			throw new MqNotAckException(r.getMsg());
		}

		return true;
	}

	/**
	 * 强平时批量撤销
	 *
	 * @param list 订单集合
	 * @return {@link boolean} true 有剩余金额，false 没有
	 * @since 2022/9/3 21:16
	 */
	public boolean cancelBatchWhenLiquidate(List<ContractOrder> list) {
		if (CollectionUtils.isEmpty(list)) {
			return false;
		}

		// 收集订单ids
		Long[] orderIds = list.stream()
				.map(ContractOrder::getId)
				.toArray(Long[]::new);
		// 批量撤销
		if (contractOrderMapper.cancelBatch(orderIds) != list.size()) {
			throw new RuntimeException("撤销订单失败");
		}

		// 如果是平仓，回滚仓位平仓中数量
		contractUtils.rollbackPositionClosingNumByOrderList(list);
		// 解冻剩余金额
		return unFreezeRestAmount(list);
	}

	/**
	 * 处理过期订单
	 *
	 * @param dto dto
	 * @since 2022/9/6 11:30
	 */
	public void expire(ContractOrderCancelDTO dto) {
		// 开启本地事务
		mysqlUtil.startLocalTx();

		try {
			ContractOrder order = getOrder(dto);

			// 如果是平仓，回滚仓位平仓中数量
			contractUtils.rollbackPositionClosingNumByOrder(order);

			// 初始化修改信息
			securityUtil.initUpdateAbout(order);
			if (contractOrderMapper.expire(order) != 1) {
				throw new MqNotAckException("设置订单过期失败");
			}

			// 开启全局事务
			SeataUtils.startGlobalTx();
			// 解冻冻结金额
			boolean hasRestAmount = unFreezeRestAmount(order);

			// 删除相关缓存
			cancelOrderCache(order);

			// 提交全局事务
			SeataUtils.commitGlobalTx();
			// 提交本地事务
			mysqlUtil.commitLocalTx();

			// 发送订单过期深度图消息
			/*contractUtils.sendDepthMap(ContractMqConstants.EXCHANGE_FANOUT_CONTRACT_ORDER_EXPIRE_DEPTH_MAP,
					null,
					order);*/

			// 如果有需要解冻的金额
			if (hasRestAmount) {
				// 发送重算强平价消息
				contractUtils.sendPositionCrossCalculate(order.getUserId());
			}

			// 发送历史订单迁移消息
			contractUtils.sendOrderMigrate(Collections.singletonList(dto.getId()));

		} catch (Throwable e) {
			// 回滚全局事务
			SeataUtils.rollbackGlobalTx();
			// 回滚本地事务
			mysqlUtil.rollbackLocalTx();

			if (e instanceof MqAckException) {
				return;
			}
			throw e;
		}
	}
}
